package com.song.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.Tracer;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.song.pojo.User;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;

/**
 * @Author 宋伟宁
 * @Date 2024/1/8
 * @Version 1.0
 **/
@RestController
@Slf4j
public class HelloController {
    private static final String RESOURCE_NAME = "hello";
    private static final String USER_RESOURCE_NAME = "USER";
    private static final String DEGRADE_RESOURCE_NAME = "degrade";
    @GetMapping("/test")
    public String test(){
        Entry entry = null;
        try {
            entry = SphU.entry(RESOURCE_NAME);
            String str = "hello info";
            log.info("========="+str+"=======");
            return  str;
        } catch (BlockException e) {
            //  限流  被流控了
            log.info("block!");
            return "被流量控制了!";
        } catch(Exception e){
            //若需要服务降级，这里需要记录业务异常
            Tracer.traceEntry(e,entry);
        }finally {
            if(entry != null){
                entry.exit();
            }
        }
        return  null;
    }


    @GetMapping("/user")
    @SentinelResource(value = USER_RESOURCE_NAME,blockHandler = "blockHandlerUser",fallback = "fallbackByUser")
    public User getUser(String id){
        int m = 11/0;
        return new User(id+"张三");
    }

    public User blockHandlerUser(String id,BlockException e){
        log.debug("熔断了！");
        return new User("被熔断了！");
    }

    public User fallbackByUser (String id){
        log.debug("异常！");
        return new User("出现了异常！");
    }

    //演示降级
    @GetMapping("/degrade")
    @SentinelResource(value = DEGRADE_RESOURCE_NAME,entryType = EntryType.IN,blockHandler = "blockHandlerDegrade")
    public User userInfo() throws  InterruptedException {
       log.debug("hello degrade");
       throw new RuntimeException("异常了！");
    }

    public User blockHandlerDegrade(BlockException e) {
        log.debug("corresponding degraded!"+e.getMessage());
        return new User("服务被降级了");
    }



    @PostConstruct
    public void initDegradeRule(){
        ArrayList<DegradeRule> list = new ArrayList<>();
        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource(DEGRADE_RESOURCE_NAME);
        // 设置降级规则：异常数
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        // 触发熔断异常数：2；实际触发熔断的异常数要比设置的多一次，详情参考源码。
        degradeRule.setCount(2);
        // 触发熔断最小异常数：2
        degradeRule.setMinRequestAmount(2);
        // 统计时长：在多长时间内，出现两次异常进行熔断；单位：毫秒 ms
        degradeRule.setStatIntervalMs(60*1000);
        // 熔断时长：单位：秒 s；一但触发熔断之后，在熔断时间内再次请求对应的接口，就会直接调用降级方法；
        // 10秒过了之后，就会进入半开状态，半开状态一但出现异常，立刻熔断；
        degradeRule.setTimeWindow(10);
        list.add(degradeRule);
        DegradeRuleManager.loadRules(list);
    }
    /**
     * 设置流控规则
     */
    @PostConstruct// 项目启动的时候，会执行被此注解描述的方法
    private static void initFlowRules(){
        // 流控规则
        ArrayList<FlowRule> rules = new ArrayList<>();
        // 流控规则对象
        FlowRule rule = new FlowRule();
        // 设置受保护的资源
        rule.setResource(RESOURCE_NAME);
        // 设置流控规则 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值；QPS的单位是秒，也就是一秒允许几次通过，超过就进行流控
        rule.setCount(1);
        rules.add(rule);

        FlowRule rule1 = new FlowRule();
        // 设置受保护的资源
        rule1.setResource(USER_RESOURCE_NAME);
        // 设置流控规则 QPS
        rule1.setGrade(RuleConstant.FLOW_GRADE_QPS);
        // 设置受保护的资源阈值；QPS的单位是秒，也就是一秒允许几次通过，超过就进行流控
        rule1.setCount(1);
        rules.add(rule1);
        // 加载配置好的规则
        FlowRuleManager.loadRules(rules);
    }
}
